//~ #include <map>
#include <list>
#include <cassert>
#include <iostream>
//~ #include <string>

#include "alvo.hh"

// ---------------------------------------------

class LOG {
public:
	LOG& operator<<(const char*s) {cout << "***LOG*** " << s << endl;return *this;}
	LOG& operator<<(const int  s) {cout << "***LOG*** " << s << endl;return *this;}
};

LOG logg;
// ---------------------------------------------

void
enter_frame (ALVO_runtime &frame, A_LAZY &LAZY) {//ALVO_frame??
	//LAZY->enter_frame(frame);
}

ALVO*
eval (ALVO_runtime &frame, ALVO * /*const*/ i) {

	//return i->eval(frame);
	
	if (i->ac == AC_LAZY) {
		return ((A_LAZY*)i)->eval(frame);
	}else	if (i->ac == AC_INTEGER) {
		A_INT &gr = (A_INT&)*i;
		frame.push(&gr);
	}else	if (i->ac == AC_EVAL) {
		A_EVAL &gr = (A_EVAL&)*i;
		frame.eval(&gr);
	}else {
		cout << "\n****\nerror(32)" << i->ac <<"\n****"<< endl;
		//~ assert(32!=32);
		//throw 77;
		//exit(117);
	}
}

ALVO *
alvo_x(ALVO_runtime &B) {
	A_LAZY* A1=B.C.lazy();
	A_INT*  A2=B.C.int_(3, B.X.cont(*A1));
	A_EVAL* A3=B.C.eval("print", B.X.cont(*A1));
	//
	return A1;
}

ALVO_X ALVO_runtime::X;
ALVO_C ALVO_runtime::C;

void A_LAZY::add(ALVO*aa) {items.push_back(aa);}
void A_LIST::add(ALVO*aa) {items.push_back(aa);}
void  A_GRP::add(ALVO*aa) {items.push_back(aa);}

ALVO* A_LAZY::eval(ALVO_runtime &frame) {
	A_LAZY &gr=*this;
	vector<ALVO*>::iterator ii=gr.items.begin(),ie=gr.items.end();

	enter_frame(frame, gr); //??
	while (ii!=ie) {

		// ------------------------------------
		logg << (*ii)->ac ;
		// ------------------------------------

		::eval(frame, *ii);
// 		(*ii)->eval(frame);
		++ii;
		//ii=gr.next;
	}
	return NULL;
}
ALVO* A_LIST::eval(ALVO_runtime &frame) {frame.push(this);return NULL;}
ALVO*  A_GRP::eval(ALVO_runtime &frame) {frame.push(this);return NULL;}

AH_CONT* ALVO_X::cont(A_CONT_TAG& ag) {
	AH_CONT* R=new AH_CONT;
	R->valid=true;
	R->c=&ag;
	return R;
}
A_GRP*  ALVO_C::group() { A_GRP * R=new A_GRP;  return R; }
A_LIST*  ALVO_C::list() { A_LIST* R=new A_LIST; return R; }
A_LAZY*  ALVO_C::lazy() { A_LAZY* R=new A_LAZY; return R; }

A_INT*   ALVO_C::int_(char* ais, AH_CONT*ak) {
	return int_(atoi(ais), ak);
}

A_INT*   ALVO_C::int_(int   ai, AH_CONT*ak) {
	A_INT* R=new A_INT;
	R->m=ai;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

AH_CONT& operator<< (AH_CONT &ak, ALVO &av) {
	ak.c->add(&av);
}

A_STR*   ALVO_C::stringular(char *as, AH_CONT*ak) {
	A_STR* R=new A_STR;
	R->m=as;
	assert(ak->valid);
	ak->c->add(R);
	// ak << R; // TODO: change method sig
	return R;
}

A_SYM*   ALVO_C::symbol(char *as, AH_CONT*ak) {
	A_SYM* R=new A_SYM;
	R->m=as;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

A_EVAL* ALVO_C::eval(char* ac, AH_CONT*ak) {
	A_EVAL* R=new A_EVAL;
	R->m=ac;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

ALVO_runtime* ALVO_runtime::base = new ALVO_runtime ();

void ALVO_runtime::push(ALVO*aa) {
	//assert(false);
	items.push_back(aa);
}

void AlvoM::print_int(int T) {printf("%d", T);}

ALVO* ALVO_runtime::pop() {
	ALVO *x=items.back();
	items.pop_back();
	return x;
}

void ALVO_runtime::eval(A_EVAL*ae) {
	string scar, face(ae->m);

	if (face == "print") {
		ALVO *x=pop();
		
		//
		if (x->ac==AC_INTEGER) {
			AlvoM::print_int(((A_INT*)x)->m);
		} else {
			assert(false);
		}
	} else if (face == "def") {
		ALVO *fnname=pop();
		ALVO *fnbody=pop();
		fns[((A_STR*)fnname)->m] = fnbody;
	} else {
		ALVO *fnname=pop();
		char *cfnname=((A_STR*)fnname)->m;
		
		fns_iter ie=fns.end(),iff;
		iff=fns.find(cfnname);
		if (iff==ie) {
			assert(135==false);
		} else {
			ALVO *p=iff->second;
			::eval(*this, p);
		}
	}
}

//~ class SymStream {
//~ public:
//~ };

//~ #include "alvop.cch"

//~ #include <unistd.h>
//~ #include <sys/types.h>
//~ #include <sys/stat.h>
//~ #include <fcntl.h>

extern "C" {extern int rest(char*,void*);}

int main(void) {
	ALVO_runtime *B=new ALVO_runtime;
//	ALVO *C=alvo_x(*B);
//	eval(*B,C);
	//
	//~ if (0) {int fd=open("fib.alvo", O_RDONLY);
	//~ if (fd==-1) {return 999;};
	//~ char s[4096];
	//~ int bytes=read(fd,s,sizeof(s)-1);
	//~ s[4096]='\0';
	//~ assert(bytes>0);
	//~ int i=0;}
	//~ //AlvoP(s,i,*new SymStream);

// 	ALVO*C=B->C.group();

A_LIST*C;
	rest("fib.alvo", &C);
	eval(*B,C);
	//
	return 0;
}
